package com.example.juc.big_talk_concurrent.chapter1.article5;

/**
 * “时间上的先发生” 与 “先行发生”
 * 上述 8 种规则中，还不断提到了时间上的先后，那么，“时间上的先发生” 与 “先行发生（Happens-before）” 到底有啥区别？
 * <p>
 * 一个操作 “时间上的先发生” 是否就代表这个操作会是“先行发生” 呢？一个操作 “先行发生” 是否就能推导出这个操作必定是“时间上的先发生”呢？
 * <p>
 * 很遗憾，这两个推论都是不成立的。
 */
public class HappensBeforeTest {
    private int value = 0;

    // 线程 A 调用
    public void setValue(int value) {
        this.value = value;
    }

    // 线程 B 调用
    public int getValue() {
        return value;
    }

}

//假设存在线程 A 和 B，线程 A 先（时间上的先后）调用了 setValue(1)，然后线程 B 调用了同一个对象的 getValue() ，那么线程 B 收到的返回值是什么？
//
//我们根据上述 Happens-before 的 8 大规则依次分析一下：
//
//由于两个方法分别由线程 A 和 B 调用，不在同一个线程中，所以程序次序规则在这里不适用；
//
//由于没有 synchronized 同步块，自然就不会发生 lock 和 unlock 操作，所以管程锁定规则在这里不适用；
//
//同样的，volatile 变量规则，线程启动、终止、中断规则和对象终结规则也和这里完全没有关系。
//
//因为没有一个适用的 Happens-before 规则，所以第 8 条规则传递性也无从谈起。
//
//因此我们可以判定，尽管线程 A 在操作时间上来看是先于线程 B 的，但是并不能说 A Happens-before B，也就是 A 线程操作的结果 B 不一定能看到。所以，这段代码是线程不安全的。
//
//想要修复这个问题也很简单？既然不满足 Happens-before 原则，那我修改下让它满足不就行了。比如说把 Getter/Setter 方法都用 synchronized 修饰，这样就可以套用管程锁定规则；再比如把 value 定义为 volatile 变量，这样就可以套用 volatile 变量规则等。
//
//这个例子，就论证了一个操作 “时间上的先发生” 不代表这个操作会是 “先行发生（Happens-before）”。

//再来看一个例子：
//
//
//// 以下操作在同一个线程中执行
//int i = 1;
//int j = 2;
//假设这段代码中的两条赋值语句在同一个线程之中，那么根据程序次序规则，“int i = 1” 的操作先行发生（Happens-before）于 “int j = 2”，但是，还记得 Happens-before 的第 2 条定义吗？还记得上文说过 JMM 实际上是遵守这样的一条原则：只要不改变程序的执行结果（指的是单线程程序和正确同步的多线程程序），编译器和处理器怎么优化都行。
//
//所以，“int j=2” 这句代码完全可能优先被处理器执行，因为这并不影响程序的最终运行结果。
//
//那么，这个例子，就论证了一个操作 “先行发生（Happens-before）” 不代表这个操作一定是“时间上的先发生”。
//
//这样，综上两例，我们可以得出这样一个结论：Happens-before 原则与时间先后顺序之间基本没有因果关系，所以我们在衡量并发安全问题的时候，尽量不要受时间顺序的干扰，一切必须以 Happens-before 原则为准。
//
